package com.rimi.util;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import com.rimi.bean.User;
import org.junit.Test;

import javax.sql.DataSource;
import java.awt.print.Book;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 *  JDBC操作数据库工具类
 * @Author luc
 * @Date 2020/6/10
 */
public class JdbcUtil {

    //     数据库连接
    private Connection connection = null;
    //     数据库预编译通道
    private PreparedStatement preparedStatement = null;

//      druid 数据源
    private static DataSource dataSource = null;

    static {
//         从类加载文件根目录读取文件
//        创建配置对象
        Properties properties = new Properties();
        try (InputStream resource = JdbcUtil.class.getClassLoader().getResourceAsStream("datasource.properties")){
            properties.load(resource);
            //         利用工厂创建druid的数据源
            dataSource =  DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     *  从druid数据源获取连接
     * @return
     */
    public Connection getConnection() {
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     *  创建预编译通道
     * @param sql   创建预编译通道时使用sql
     * @return
     */
    public PreparedStatement getPreparedStatement(String sql) {

        try {
            preparedStatement = connection.prepareStatement(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return preparedStatement;
    }

    /**
     *  传入参数数据，绑定到预编译通道
     * @param params
     */
    public void bundle(Object... params) {
//      遍历参数数组，默认数组的第一个对象，对应第一个?,依次类推
        try {
            for (int index = 0; index < params.length; index++) {
                preparedStatement.setObject(index + 1, params[index]);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     *  根据传入的sql，以及参数列表数组 进行查询
     * @param sql   查询语句
     * @param params  ？占位符的需要用来绑定的数据
     *                  ... 不定数组， 当前是一个数组，数组长度可以是0个，1个，。。。无限个，由方法调用决定
     *
     * @return
     */
    public <T> List<T> query(Class<T> clazz, String sql, Object... params) {

       // 准备connection
        getConnection();
        // 准备预编译通道
        getPreparedStatement(sql);
        // 绑定参数
        bundle(params);
        // 执行sql
        ResultSet resultSet = null;

//        创建空的list，类型为泛型T，由真实的参数决定
        List<T> lists = new ArrayList<>();

        try {
//             查询结果
            resultSet = preparedStatement.executeQuery();
//            元数据, 对列信息进行描述
            ResultSetMetaData metaData = resultSet.getMetaData();

            String[] filedNames = new String[metaData.getColumnCount()];
//            getColumnCount获取列的总数
            for(int index = 0; index < metaData.getColumnCount(); index ++) {
//                getColumnName获取列名，下标从1 开始
//                将列名转换成set方法名
                filedNames[index] = changeToUpperFirstFiledName(metaData.getColumnName(index+1));
            }

            while(resultSet.next()) {
//                每次循环找到一行数据，封装成对应的java bean ，放到lists列表
                T obj = clazz.newInstance();

                for(int index = 0; index < metaData.getColumnCount(); index++) {
//
//                    获取每一列的数据
                    Object object = resultSet.getObject(index+1);
//                    获取当前列对应的方法名
                    String filedName = filedNames[index];
//                    获取当前类的属性
                    Field field = clazz.getDeclaredField(filedName);
//                    设置可访问权限
                    field.setAccessible(true);
//                  给当前行对象赋值
                    field.set(obj, object);
                }

//                obj 设置完毕，放入list
                lists.add(obj);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(resultSet,preparedStatement,connection);
        }

        return lists;
    }

    /**
     *  执行count类的统计sql
     * @param sql
     * @param params
     * @return
     */
    public int count(String sql, Object... params) {
        // 准备connection
        getConnection();
        // 准备预编译通道
        getPreparedStatement(sql);
        // 绑定参数
        bundle(params);
        // 执行sql
        ResultSet resultSet = null;

        try {
            resultSet = preparedStatement.executeQuery();

            while(resultSet.next()) {
                return resultSet.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(resultSet,preparedStatement,connection);
        }

        return -1;


    }


    /**
     *  JDBC update 更新
     * @param sql   更新的sql
     * @param parmas  需要绑定的参数
     * @return  返回受影响的行
     */
    public int update(String sql, Object... parmas) {

        getConnection();

        getPreparedStatement(sql);

        bundle(parmas);

        try {
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(null,preparedStatement,connection);
        }

        return -1;
    }
    /**
     *  传入一个字符串，进行驼峰转换
     * @param name   需要被转换的字符串
     * @return
     */
    public static String changeToUpperFirstFiledName(String name) {
//      用下划线切割
        String[] split = name.split("_");

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(split[0]);
//      切割完成，首字符大小，然后拼接
        for(int index = 1; index < split.length; index++) {
            stringBuilder.append(split[index].substring(0,1).toUpperCase()).append(split[index].substring(1));
        }
        return stringBuilder.toString();
    }

    /**
     *  传入一个java对象，返回该对象
     *      <T> 字母是任意取
     *          如果用于方法，声明当前方法为泛型方法,说明在当前方法中，在编译时，如果存在同样的这个字母，需要按照具体类型进行替换
     *
     */
    public <T> T get(T object) {
        return object;
    }

    /**
     *  单元测试方法
     *      作用于public 无参，无返回值的方法
     *      不要用于多线程
     */
    @Test
    public void test() {
        String sql = "insert into user(id, username,password) values(?,?,?)";

        JdbcUtil jdbcUtil = new JdbcUtil();
        jdbcUtil.update(sql,null,"szc","123");
    }

    @Test
    public void test2() {
        String sql = "select * from user";

        JdbcUtil jdbcUtil = new JdbcUtil();
        List<User> query = jdbcUtil.query(User.class, sql);
//        List<Book> query1 = jdbcUtil.query(Book.class, sql);
        System.out.println(query);
    }


    @Test
    public void test3() {

        try {

            /**
             *  反射创建对象
             *      创建对象的逻辑步骤
             *          1. 找到对象的字节码
             *              "com.rimi.bean.User"
             *          2. 将该对象字节码放入方法区
             *              Class.forName("com.rimi.bean.User")
             *          3. 根据方法区中字节码，找一个合适的构造方法
             *              .getConstructor()
             *          4. 在堆中分配内存空间，创建对象,初始化
             *              .newInstance()
             *          5. 将堆中对象地址交给栈空间引用变量
              */

            User user = new User(1,"qaz","123");
            System.out.println(user);

            Class<?> userc = Class.forName("com.rimi.bean.User");
            Object user2 = userc.getConstructor(Integer.class,String.class,String.class).newInstance(3,"edc","321");
            System.out.println(user2);

            /**
             *  反射属性
             *      1. 获取属性
             *          getFields/getField   获取所有的公有的属性(包括父亲的公有属性),类似于user.test
             *          getDeclaredFields/getDeclaredField  获取类本身所有的属性，包括私有
             *      2. 属性赋值
             *          field.set(object,123)  等价于 object.field=123
             *          其中 field代表给哪一个属性赋值
             *              object 代表操作哪一个对象
             *      3. 属性取值
             *          field.get(object) 等价于object.field;
             *          其中 field代表给哪一个属性赋值
             *               object 代表操作哪一个对象
             *      4. 属性权限修改
             *           setAccessible
             *
             */



            user.test = 1;
            System.out.println(user);

//            Field[] fields = userc.getFields();

//            Field[] fields = userc.getDeclaredFields();
//            for (Field field : fields) {
//                System.out.println(field);
//            }

            Field field = userc.getField("test");

            field.set(user2,123);
            System.out.println(user2);
            System.out.println(field.get(user2));


            Field id = userc.getDeclaredField("id");
            id.setAccessible(true);
                 id.set(user2,4);

            System.out.println(user2);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }

    }

    public void close(ResultSet resultSet, Statement statement, Connection connection) {

        if(null != resultSet) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(null != statement) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(null != connection) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }


}
